package scaladuct

import javax.validation.{ValidationException, ConstraintViolation, Validation, ValidatorFactory}
import java.lang.reflect.Type

/**
 * <p>
 * Created by IntelliJ IDEA.
 * User: zslajchrt
 * Date: Jun 28, 2010
 * Time: 8:33:27 PM
 */

@serializable @SerialVersionUID(1L)
sealed abstract class ConfigResult[X]
final case class NextPage[X](page: ConfigPage[X]) extends ConfigResult[X]
final case class NewStarter[X](starter: Starter[X]) extends ConfigResult[X]

trait ConfigurableFactory[X] extends Extensible {
  final type InstanceType = X
  type Cookie <: AnyRef
  def id: String

  def getCookie(): Cookie;

  def consume(cookie: Cookie): ConfigResult[InstanceType]

  def unconsume(result: ConfigResult[X]): Unit

  def canUnconsume(result: ConfigResult[X]): Boolean

  def cancel(): Unit
}

@serializable @SerialVersionUID(1L)
trait ConfigPageFactory[X] {

  def createConfigPage(pageInstanceName: String, lookup: LookupFunctions.LookupFunction, initArgs: AnyRef*): ConfigPage[X]
}

//abstract class ConfigPage[X] extends ConfigurableFactory[X] {
@serializable @SerialVersionUID(1L)
trait ConfigPage[X] extends ConfigurableFactory[X] {
  override def toString = getClass.getName + " id:" + id

  require(id != null && id != "", "Config pages must have non-null and non-empty id. " + this)

  def cancel() = null

  def consumeRawCookie(anyRef: AnyRef): ConfigResult[InstanceType] = {
    consume(anyRef.asInstanceOf[Cookie])
  }

  def unconsume(result: ConfigResult[X]): Unit = throw new UnsupportedOperationException("unconsumption not supported")

  def canUnconsume(result: ConfigResult[X]): Boolean = false

  def |>[Y](decoratedPage: ConfigPage[Y]): ConfigPage[Y] = new PreDecoratedPage(this, decoratedPage)

  def |>[Y](decoratedPage: ConfigPage[Y], decorationCallback: (Starter[InstanceType]) => Unit): ConfigPage[Y] = new PreDecoratedPage(this, decoratedPage, decorationCallback)

  def <|[Y](decoratingPage: ConfigPage[Y]): ConfigPage[InstanceType] = new PostDecoratedPage(this, decoratingPage)

  def <|[Y](decoratingPage: ConfigPage[Y], decorationCallback: (Starter[Y]) => Unit): ConfigPage[InstanceType] = new PostDecoratedPage(this, decoratingPage, decorationCallback)

  override def equals(obj: Any) = obj match {
    case that: ConfigPage[_] => {
      (that canEqual this) && that.id == id
    }
    case _ => false
  }

  def canEqual(other: Any): Boolean =
    other.isInstanceOf[ConfigPage[_]]

  override def hashCode = id.hashCode
}

object ConfigPage {
  def apply[X](configClass: Class[_]): ConfigPage[X] = {
    // todo
    null
  }
}

abstract class NamedConfigPage[X](pageName: String, pageClass: Class[_]) extends ConfigPage[X] {
  def id = pageName + "." + pageClass.getName
}

abstract class AbstractConfigPage[X] extends ConfigPage[X] 

object ConfigPageTO {
  def apply(page: ConfigPage[_]): ConfigPageTO = apply(page, null)

  def apply(page: ConfigPage[_], parentPage: ConfigPageTO): ConfigPageTO = new ConfigPageTO(page.id, parentPage)

  def apply(pages: Seq[ConfigPage[_]]): ConfigPageTO = pages.toList match {
    case List() => null
    case head :: tail => apply(head, apply(tail))
  }
}
class ConfigPageTO(val pageId: String, val parentPage: ConfigPageTO)

@serializable @SerialVersionUID(1L)
sealed class Terminal
case object Terminal extends Terminal

abstract class FinalPage[X] extends ConfigPage[X] {
  type Cookie = Terminal

  def getCookie() = Terminal

  final def consume(cookie: Terminal) = {
    NewStarter(consume)
  }

  protected def consume(): Starter[X]
}

class FinishedConfigPage[X](val id: String, val starter: Starter[X]) extends ConfigPage[X] with ExtensibleDecorator {

  def decoratedForExtensible = starter

  type Cookie = Terminal

  def getCookie() = Terminal

  def consume(cookie: Cookie) = NewStarter(starter)

}

/**
 * Indicates the page can provide the type of the instance which the page is designed to instantiate
 * (by means of a starter).
 */
trait HasProductType {
  def getProductType(): Type
}